package net.yters.model.metric;

import java.util.ArrayList;
import java.util.regex.Pattern;

import net.yters.model.agents.DeciderAgent;
import net.yters.util.Box;
import net.yters.util.Util.Clusterer;
import net.yters.util.Util.Hyp_TF;
import cern.colt.matrix.DoubleFactory1D;
import cern.colt.matrix.DoubleFactory2D;
import cern.colt.matrix.DoubleMatrix1D;
import cern.colt.matrix.DoubleMatrix1DProcedure;
import cern.colt.matrix.DoubleMatrix2D;
import cern.colt.matrix.linalg.Algebra;

public class ClusterRatioMetric extends Metric {
	private static final long serialVersionUID = 5702627193613790229L;
	
	public Box f_headers = new Box(null);
	public Box f_history = new Box(null);

	public Box f_time = null;

	public ClusterRatioMetric(HistoryCollector collector) {
		f_headers = collector.f_headers;
		f_history = collector.f_history;
		f_time = collector.f_time;
	}

	@Override
	public void execute() {
		if((Double) f_ownerAgent.f_alive.f_value != 1) {
			return; // Exit if agent is dead, since it won't be making any decisions.
		}
		
		DoubleMatrix2D history = (DoubleMatrix2D) f_history.f_value;
		DeciderAgent ownerAgent = f_ownerAgent;
		DoubleMatrix2D agentView = regexSelect(".*" + ownerAgent.f_name + ".*", history);
		DoubleMatrix2D sensorView = regexSelect(".*" + "sensors" + ".*", agentView);
		Clusterer clusterer = new Clusterer(new Hyp_TF(0.3, 1)); // TODO should use a different test function that doesn't depend on # of samples

		double[] sensors_cl = DoubleFactory1D.dense.make(history.rows()).toArray();
		double[][] sensors_ch = DoubleFactory2D.dense.make(history.rows(), sensorView.columns()).toArray();
		double[][] sensors_lst = sensorView.toArray();
		if(sensors_lst.length < 2) {
			f_result.f_value = 1.0;
			return;
		}
		clusterer.clusterer_init(sensors_cl, sensors_ch, 0, sensors_lst);
		sensors_cl = clusterer.get_cl();
		sensors_ch = clusterer.get_ch();

		DoubleMatrix2D stateView = regexUnSelect(".*(choice|type|alive|actuators|sensors).*", sensorView);
		double[] state_cl = DoubleFactory1D.dense.make(history.rows()).toArray();
		double[][] state_ch = DoubleFactory2D.dense.make(history.rows(), history.columns()).toArray();
		double[][] state_lst = stateView.toArray();
		if(state_lst.length < 2) {
			f_result.f_value = 1.0;
			return;
		}
		clusterer.clusterer_init(state_cl, state_ch, 0, state_lst);
		state_cl = clusterer.get_cl();
		state_ch = clusterer.get_ch();
		
		f_result.f_value = (double) (state_ch.length/sensors_cl.length);
	}

	@SuppressWarnings("unchecked")
	DoubleMatrix2D regexSelect(final String r, final DoubleMatrix2D m) { 
		final ArrayList<String> headers = (ArrayList<String>) f_headers.f_value;
		Algebra F = new Algebra();
		DoubleMatrix2D result = F.transpose(F.transpose(m)
				.viewSelection(
						new DoubleMatrix1DProcedure() {
							int counter = 0;
							public final boolean apply(DoubleMatrix1D unused) {
								if(counter == m.columns()) {
									counter = 0;
								}
								if(Pattern.matches(r, headers.get(counter++))) { // TODO precompile the Pattern to increase efficiency
									return true;
								}
								return false;
							}
						}));
		return result;
	}

	@SuppressWarnings("unchecked")
	DoubleMatrix2D regexUnSelect(final String r, final DoubleMatrix2D m) { 
		final ArrayList<String> headers = (ArrayList<String>) f_headers.f_value;
		Algebra F = new Algebra();
		DoubleMatrix2D result = F.transpose(F.transpose(m)
				.viewSelection(
						new DoubleMatrix1DProcedure() {
							int counter = 0;
							public final boolean apply(DoubleMatrix1D unused) {
								if(counter == m.columns()) {
									counter = 0;
								}
								if(Pattern.matches(r, headers.get(counter++))) {
									return false;
								}
								return true;
							}
						}));
		return result;
	}
}
